defmodule DbSupport.Provider.Mnesia do
  @behaviour DbSupport.Provider

  #require Logger
  #alias Logger, as: LOG
  alias :mnesia, as: Mnesia

  @impl DbSupport.Provider
  def init_master() do
    #LOG.debug("init_master()")
    Mnesia.stop()
    node_list = [node()]
    Mnesia.create_schema(node_list)
    Mnesia.start()
    #IO.inspect Mnesia.info
    table_names = create_tables([
                      #{Spider.Counter, :set},
                      #Spider.Page
                      {Ziply.Counter, :set},
                      Ziply.Links
                    ], node_list)
    #LOG.debug("wait_for_tables ->")
    Mnesia.wait_for_tables(table_names, 5_000)
    #LOG.debug("<- wait_for_tables()")
    IO.inspect Mnesia.info
  end

  defp create_tables(modules, node_list) do
    Enum.map(modules, fn module ->
      create_table(module, node_list)
    end)
  end

  defp create_table(module, node_list) do
    #IO.pust module
    #LOG.debug("create_table: #{module} at #{node_list}")
    table_name = get_table_name(module)

    options = 
      get_table_options(module)
      |> Enum.concat([disc_copies: node_list])

    Mnesia.create_table(table_name, options)

    table_name
  end

  defp get_table_name({module, _type}) do
    get_table_name(module)
  end

  defp get_table_name(module) do
    module.__schema__(:source)
      |> String.to_atom
  end

  defp get_table_options({module, type}) do
    get_table_options(module)
      |> Enum.concat([type: type])
  end

  defp get_table_options(module) do
    [
      record_name: module,
      attributes: module.__schema__(:fields)
    ]
  end


  @impl DbSupport.Provider
  def get_data_by_id(table, id) do
    Mnesia.transaction(fn ->
      Mnesia.read(table, id)
    end)
  end

  @impl DbSupport.Provider
  def get_all(table) do
    Mnesia.transaction(fn ->
      Mnesia.match_object(Mnesia.table_info(table, :wild_pattern))
    end)
  end

  @impl DbSupport.Provider
  def create_data(table, data) do
    Mnesia.transaction(fn ->
      record = Tuple.insert_at(data, 0, table)
      Mnesia.write(record)
    end)
  end

  
  @impl DbSupport.Provider
  def next_id(type) do
    {:atomic, next_id} = Mnesia.transaction(fn ->
      existing_rec = Mnesia.read(:counter, type, :write)

      case existing_rec do
        [] ->
          Mnesia.write(:counter, {Ziply.Counter, type, 1}, :write)
          1
        [{Ziply.Counter, type, existing_id}] ->
          Mnesia.write(:counter, {Ziply.Counter, type, existing_id + 1}, :write)
          existing_id + 1
      end
    end)

    next_id
  end

  @tags [:starwar, :sexy, :woman, :sport, :comics]
  def create_content(id, title, content) do
    Mnesia.transaction(fn ->
      index = Integer.mod(id, 5)
      Mnesia.write({:content, id, title, content, [Enum.at(@tags, index)]})
    end)
  end


  @impl DbSupport.Provider
  def add_self_to_cluster(master_node) do
    Node.connect(master_node)

    Mnesia.start()

    :rpc.call(master_node, T1.Provider.Mnesia, :add_child_to_cluster, [node()])

    Mnesia.add_table_copy(:content, node(), :disc_copies)
  end

  def add_child_to_cluster(child_node) do
    Mnesia.change_config(:extra_db_nodes, [child_node])

    Mnesia.change_table_copy_type(:schema, child_node, :disc_copies)
  end

end
